﻿using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using xbmc_json_async.Media;
using xbmc_json_async.System;

using xbmc_json_async.Types;
using System;

namespace xbmc_json_async.Library
{
    internal enum VideoLibrary
    {
        Clean,
        Export,
        GetEpisodeDetails,
        GetEpisodes,
        GetGenres,
        GetMovieDetails,
        GetMovieSetDetails,
        GetMovieSets,
        GetMovies,
        GetMusicVideoDetails,
        GetMusicVideos,
        GetRecentlyAddedEpisodes,
        GetRecentlyAddedMovies,
        GetRecentlyAddedMusicVideos,
        GetSeasons,
        GetTVShowDetails,
        GetTVShows,
        Scan
    }
    
    public class XVideoLibrary
    {
        /*Specification: http://wiki.xbmc.org/index.php?title=JSON-RPC_API/v4#VideoLibrary
         *API Methods
         *  VideoLibrary.Clean (implemented)
         *  VideoLibrary.Export (not implemented)
         *  VideoLibrary.GetEpisodeDetails (implemented)
         *  VideoLibrary.GetEpisodes (implemented)
         *  VideoLibrary.GetGenres (implemented)
         *  VideoLibrary.GetMovieDetails (implemented)
         *  VideoLibrary.GetMovieSetDetails (implemented)
         *  VideoLibrary.GetMovieSets (implemented)
         *  VideoLibrary.GetMovies (implemented)
         *  VideoLibrary.GetMusicVideoDetails (implemented)
         *  VideoLibrary.GetMusicVideos (implemented)
         *  VideoLibrary.GetRecentlyAddedEpisodes (implemented)
         *  VideoLibrary.GetRecentlyAddedMovies (implemented)
         *  VideoLibrary.GetRecentlyAddedMusicVideos (implemented)
         *  VideoLibrary.GetSeasons (implemented)
         *  VideoLibrary.GetTVShowDetails (implemented)
         *  VideoLibrary.GetTVShows (implemented)
         *  VideoLibrary.Scan (implemented)
         * 
         */

        private readonly XClient mClient;
        private XError mError;

        private string mPropType = "properties";
        private string mLibraryName = "VideoLibrary";

        /// <summary>
        /// Initializes a new instance of the <see cref="XVideoLibrary"/> class.
        /// </summary>
        /// <param name="client">The client.</param>
        public XVideoLibrary(XClient client)
        {
            mClient = client;
        }

        #region publicmethods
        /// <summary>
        /// Cleans the audio library of non existent items.
        /// </summary>
        /// <param name="userCallback">The user callback.</param>
        public void Clean(XDataReceived userCallback)
        {
            mClient.GetData(SetMethod(VideoLibrary.Clean), null, VideoLibraryCallbackManager, userCallback);
        }

        /// <summary>
        /// Exports the specified export path.
        /// </summary>
        /// <param name="exportPath">The export path.</param>
        /// <param name="includeImages">if set to <c>true</c> [include images].</param>
        /// <param name="overwrite">if set to <c>true</c> [overwrite].</param>
        /// <param name="userCallback">The user callback.</param>
        public void Export(string exportPath, bool includeImages, bool overwrite, XDataReceived userCallback)
        {
            throw new NotImplementedException("Not supported due to JSON complexity");
            /*var args = new JObject(
                new JProperty("type",
                    new JArray(
                        new JObject(new JProperty("path", exportPath)),
                        new JObject(
                            new JProperty("images", includeImages),
                            new JProperty("overwrite", overwrite)

                            )
                        )
                    )
                );

            */


            //var args = new JObject(
            //new JProperty("type",
            //    new JObject(
            //        new JObject("properties",
            //            new JProperty("path",exportPath)

            //            )

            //        ),
            //    new JObject(
            //         new JObject("properties",
            //             new JProperty("images", includeImages),
            //             new JProperty("overwrite", overwrite)

            //        )


            //    )
            //));
            /*var args = new JObject(new JProperty("properties",
                    new JArray(
                    new JObject(new JProperty("path", exportPath)),
                    new JObject(new JProperty("images", includeImages)),
                    new JObject(new JProperty("overwrite", overwrite)))));*/

            //mClient.GetData("VideoLibrary.Export", args, ExportCallback, userCallback);
        }

        /// <summary>
        /// Exports the callback.
        /// </summary>
        /// <param name="requestState">State of the request.</param>
        private void ExportCallback(XRequestState requestState)
        {
            var query = JObject.Parse(requestState.ResponseData);

            if (XHelpers.CheckResponseForError(query, out mError))
            {

            }
            if (requestState.UserCallback != null)
            {
                requestState.UserCallback(query);
                //var result = (JObject)query["result"];
            }
        }

        /// <summary>
        /// Gets the episode details.
        /// </summary>
        /// <param name="episodeId">The episode id.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetEpisodeDetails(int episodeId, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty("episodeid", episodeId), new JProperty(mPropType, Types.Video.Fields.Episode.Get()) };
            mClient.GetData(SetMethod(VideoLibrary.GetEpisodeDetails), args, VideoLibraryCallbackManager, userCallback);
        }

        /// <summary>
        /// Gets the episodes.
        /// </summary>
        /// <param name="tvShowId">The tv show id.</param>
        /// <param name="season">The season.</param>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetEpisodes(int tvShowId, int season, SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject
                           {
                               new JProperty("tvshowid", tvShowId),
                               new JProperty("season", season),
                               new JProperty(mPropType, Types.Video.Fields.Episode.Get())
                           };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData(SetMethod(VideoLibrary.GetEpisodes), args, VideoLibraryCallbackManager, userCallback);
        }

        /// <summary>
        /// Gets the genres.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetGenres(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty(mPropType, Types.Library.Fields.Genre.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData(SetMethod(VideoLibrary.GetGenres), args, VideoLibraryCallbackManager, userCallback);
        }

        /// <summary>
        /// Gets the movie details.
        /// </summary>
        /// <param name="movieId">The movie id.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetMovieDetails(int movieId, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty("movieid", movieId), new JProperty(mPropType, Types.Video.Fields.Movie.Get()) };
            mClient.GetData(SetMethod(VideoLibrary.GetMovieDetails), args, VideoLibraryCallbackManager, userCallback);
        }
   
        /// <summary>
        /// Gets the movies.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetMovies(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty(mPropType, Types.Video.Fields.Movie.Get()) };
            
            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData(SetMethod(VideoLibrary.GetMovies), args, VideoLibraryCallbackManager, userCallback);
        }

        /// <summary>
        /// Gets the movie set details.
        /// </summary>
        /// <param name="setId">The set id.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetMovieSetDetails(int setId, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty("setid", setId), new JProperty(mPropType, Types.Video.Fields.MovieSet.Get()) };
            mClient.GetData(SetMethod(VideoLibrary.GetMovieSetDetails), args, VideoLibraryCallbackManager, userCallback);
        }

        /// <summary>
        /// Gets the movie sets.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetMovieSets(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty(mPropType, Types.Video.Fields.MovieSet.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData(SetMethod(VideoLibrary.GetMovieSets), args, VideoLibraryCallbackManager, userCallback);
        }

        /// <summary>
        /// Gets the music video details.
        /// </summary>
        /// <param name="videoId">The video id.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetMusicVideoDetails(int videoId, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty("musicvideoid", videoId), new JProperty(mPropType, Types.Video.Fields.MusicVideo.Get()) };
            mClient.GetData(SetMethod(VideoLibrary.GetMusicVideoDetails), args, VideoLibraryCallbackManager, userCallback);
        }

        /// <summary>
        /// Gets the music videos.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetMusicVideos(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty(mPropType, Types.Video.Fields.MusicVideo.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData(SetMethod(VideoLibrary.GetMusicVideos), args, VideoLibraryCallbackManager, userCallback);
        }

        /// <summary>
        /// Gets the recently added episodes.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetRecentlyAddedEpisodes(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject {new JProperty(mPropType, Types.Video.Fields.Episode.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData(SetMethod(VideoLibrary.GetRecentlyAddedEpisodes), args, VideoLibraryCallbackManager, userCallback);
        }
        
        /// <summary>
        /// Gets the recently added movies.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetRecentlyAddedMovies(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty(mPropType, Types.Video.Fields.Movie.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData(SetMethod(VideoLibrary.GetRecentlyAddedMovies), args, VideoLibraryCallbackManager, userCallback);

        }

        /// <summary>
        /// Gets the recently added music videos.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetRecentlyAddedMusicVideos(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty(mPropType, Types.Video.Fields.MusicVideo.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData(SetMethod(VideoLibrary.GetRecentlyAddedMusicVideos), args, VideoLibraryCallbackManager, userCallback);
        }
       
        /// <summary>
        /// Gets the seasons.
        /// </summary>
        /// <param name="tvShowId">The tv show id.</param>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetSeasons(int tvShowId, SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty("tvshowid", tvShowId), new JProperty(mPropType, Types.Video.Fields.Season.Get()) };

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData(SetMethod(VideoLibrary.GetSeasons), args, VideoLibraryCallbackManager, userCallback);
        }

        /// <summary>
        /// Gets the tv show details.
        /// </summary>
        /// <param name="tvShowId">The tv show id.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetTvShowDetails(int tvShowId, XDataReceived userCallback)
        {
            var args = new JObject { new JProperty("tvshowid", tvShowId), new JProperty(mPropType, Types.Video.Fields.TVShow.Get()) };
            mClient.GetData(SetMethod(VideoLibrary.GetTVShowDetails), args, VideoLibraryCallbackManager, userCallback);
        }

        /// <summary>
        /// Gets the tv shows.
        /// </summary>
        /// <param name="sort">The sort.</param>
        /// <param name="userCallback">The user callback.</param>
        public void GetTvShows(SortParams sort, XDataReceived userCallback)
        {
            var args = new JObject {new JProperty(mPropType, Types.Video.Fields.TVShow.Get())};

            if (sort != null)
                args.Add(sort.ToJObject().Children());

            mClient.GetData(SetMethod(VideoLibrary.GetTVShows), args, VideoLibraryCallbackManager, userCallback);
        }

        /// <summary>
        /// Scans the specified user callback.
        /// </summary>
        /// <param name="userCallback">The user callback.</param>
        public void Scan(XDataReceived userCallback)
        {
            mClient.GetData(SetMethod(VideoLibrary.Scan), null, VideoLibraryCallbackManager, userCallback);
        }
        #endregion

        #region utils
        /// <summary>
        /// Sets the method.
        /// </summary>
        /// <param name="method">The method.</param>
        /// <returns></returns>
        private string SetMethod(VideoLibrary method)
        {
            return mLibraryName + "." + method.ToString();
        }

        /// <summary>
        /// Gets the method.
        /// </summary>
        /// <param name="method">The method.</param>
        /// <returns></returns>
        private VideoLibrary GetMethod(string method)
        {
            //first of all, remove base library type
            string methodName = method.Replace(mLibraryName + ".", "");

            VideoLibrary operation = (VideoLibrary)Enum.Parse(typeof(VideoLibrary), methodName);

            return operation;
        }
        #endregion
        
        /// <summary>
        /// Videoes the library callback manager.
        /// </summary>
        /// <param name="requestState">State of the request.</param>
        private void VideoLibraryCallbackManager(XRequestState requestState)
        {
            object returnObject = null;

            VideoLibrary operation = GetMethod(requestState.RequestOperation);

            var query = JObject.Parse(requestState.ResponseData);
            JObject result = (JObject)query["result"];
            
            
            switch (operation)
            {
                case VideoLibrary.Clean:
                    returnObject = result.Values(0).ToString();
                    break;

                case VideoLibrary.Export:
                    break;

                case VideoLibrary.GetEpisodeDetails:
                    Episode episode = null;
                    if (result["episodedetails"] != null)
                    {
                        JObject item = (JObject)result["episodedetails"];
                        episode = Episode.FromJsonObject(item);

                    }
                    returnObject = episode;
                    break;

                case VideoLibrary.GetEpisodes:
                case VideoLibrary.GetRecentlyAddedEpisodes:
                    var episodes = new Episodes();
                    episodes.LoadFromJsonObject(result);
                    returnObject = episodes;
                    break;

                case VideoLibrary.GetGenres:
                    var genres = new Genres();
                    genres.LoadFromJsonObject(result);
                    returnObject = genres;
                    break;

                case VideoLibrary.GetMovieDetails:
                    Movie movie = null;
                    if (result["moviedetails"] != null)
                    {
                        JObject item = (JObject)result["moviedetails"];
                        movie = Movie.FromJsonObject(item);
                    }
                    returnObject = movie;
                    break;

                case VideoLibrary.GetMovies:
                case VideoLibrary.GetRecentlyAddedMovies:
                    var movies = new Movies();
                    movies.LoadFromJsonObject(result);
                    returnObject = movies;
                    break;

                case VideoLibrary.GetMovieSetDetails:
                    MovieSetExtended movieSet = null;
                    if (result["setdetails"] != null)
                    {
                        JObject item = (JObject)result["setdetails"];
                        movieSet = MovieSetExtended.FromJsonObject(item);
                    }
                    returnObject = movieSet;
                    break;

                case VideoLibrary.GetMovieSets:
                    var movieSets = new MovieSets();
                    movieSets.LoadFromJsonObject(result);
                    returnObject = movieSets;
                    break;

                case VideoLibrary.GetMusicVideoDetails:
                    MusicVideo musicVideo = null;
                    if (result["musicvideodetails"] != null)
                    {
                        JObject item = (JObject)result["musicvideodetails"];
                        musicVideo = MusicVideo.FromJsonObject(item);
                    }
                    returnObject = musicVideo;
                    break;

                case VideoLibrary.GetMusicVideos:
                case VideoLibrary.GetRecentlyAddedMusicVideos:
                    var musicVideos = new MusicVideos();
                    musicVideos.LoadFromJsonObject(result);
                    returnObject = musicVideos;
                    break;

                case VideoLibrary.GetSeasons:
                    var seasons = new Seasons();
                    seasons.LoadFromJsonObject(result);
                    returnObject = seasons;
                    break;

                case VideoLibrary.GetTVShowDetails:
                    TvShow tvShow = null;
                    if (result["tvshowdetails"] != null)
                    {
                        JObject item = (JObject)result["tvshowdetails"];
                        tvShow = TvShow.FromJsonObject(item);
                    }
                    returnObject = tvShow;
                    break;

                case VideoLibrary.GetTVShows:
                    var tvShows = new TvShows();
                    tvShows.LoadFromJsonObject(result);
                    returnObject = tvShows;
                    break;

                case VideoLibrary.Scan:
                    returnObject = result.Values(0).ToString();
                    break;
            }
            

            if (requestState.UserCallback != null)
                requestState.UserCallback(returnObject);


        }
    }
}